use std::fmt::{Display, Write};

/// Replace {} and {name} placeholders
pub fn formatdy_impl(
    fmt: &str,
    positional: &[&dyn Display],
    named: &[(&str, &dyn Display)],
) -> String {
    let mut result = String::new();
    let mut pos_index = 0;
    let mut chars = fmt.chars().peekable();

    while let Some(c) = chars.next() {
        if c == '{' {
            let mut placeholder = String::new();
            while let Some(&ch) = chars.peek() {
                if ch == '}' {
                    chars.next();
                    break;
                }
                placeholder.push(ch);
                chars.next();
            }

            if placeholder.is_empty() {
                if pos_index < positional.len() {
                    write!(&mut result, "{}", positional[pos_index]).unwrap();
                    pos_index += 1;
                } else {
                    result.push_str("{}");
                }
            } else {
                let mut replaced = false;
                for (name, val) in named {
                    if *name == placeholder {
                        write!(&mut result, "{}", val).unwrap();
                        replaced = true;
                        break;
                    }
                }
                if !replaced {
                    write!(&mut result, "{{{}}}", placeholder).unwrap();
                }
            }
        } else {
            result.push(c);
        }
    }

    result
}

/// Macro to emulate format! style with positional and named arguments
#[macro_export]
macro_rules! formatdy {
    // positional only
    ($fmt:expr $(, $args:expr)*) => {
        $crate::utils::str::formatdy_impl($fmt, &[$(&$args),*], &[])
    };
    // positional + named
    ($fmt:expr; $pos:tt; $($name:ident = $val:expr),* $(,)?) => {
       $crate::utils::str::formatdy_impl(
            $fmt,
            &$pos,
            &[$((stringify!($name), &$val)),*]
        )
    };
}
